package org.benjamin.sso.controller;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.RandomStringGenerator;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.benjamin.sso.domain.CasServiceResponseAuthenticationFailure;
import org.benjamin.sso.domain.CasServiceResponseAuthenticationSuccess;
import org.benjamin.sso.domain.LoginBean;
import org.benjamin.sso.domain.RegisterBean;
import org.benjamin.sso.domain.entity.RegisteredService;
import org.benjamin.sso.domain.entity.TicketValidate;
import org.benjamin.sso.domain.entity.UserAuthority;
import org.benjamin.sso.domain.entity.UserInfo;
import org.benjamin.sso.repository.RegisteredServiceRepository;
import org.benjamin.sso.repository.TicketValidateRepository;
import org.benjamin.sso.repository.UserAuthorityRepository;
import org.benjamin.sso.repository.UserInfoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.util.WebUtils;

@Controller
public class LoginController {
	
	private Logger logger = org.apache.logging.log4j.LogManager.getLogger();
	
	@Autowired
	private UserInfoRepository userInfoRepository;
	
	@Autowired
	private UserAuthorityRepository userAuthorityRepository;

	@Autowired
	private TicketValidateRepository ticketValidateRepository;
	
	@Autowired
	private RegisteredServiceRepository registeredServiceRepository;
	
	/**
	 * 
	 * @param service 需要登录服务的链接
	 * @param token 用来信任Token是否是子系统的可信凭据
	 * 
	 * @return
	 * @throws MalformedURLException
	 */
	
	@GetMapping(value = "login")
	public ModelAndView login(String service,HttpServletRequest request) throws MalformedURLException {
		ModelAndView modelAndView = new ModelAndView("login");
		if(StringUtils.isNotBlank(service)) {
			List<RegisteredService> registeredServices = this.registeredServiceRepository.findByServiceUrlLike("%"+service+"%");
			if(registeredServices.isEmpty()) {
				throw new IllegalAccessError("校验网站不成功");
			}
			Subject subject = SecurityUtils.getSubject();
			if(subject.isAuthenticated()) {
				logger.debug("检测SSO系统登录过");
				RedirectView redirectView = new RedirectView(service);
				Object temp = WebUtils.getSessionAttribute(request, "ticket");
				String ticket = "";
				if(temp == null) {
					logger.debug("-----");
				}else {
					ticket = temp.toString();
				}
				logger.debug("当前SSO系统已经登录成功，直接返回服务系统进行登录，ticket：{}",ticket);
				modelAndView.setView(redirectView);
				modelAndView.addObject("ticket",ticket);
				return modelAndView;
			}
			modelAndView.addObject("service",service);
		}
		modelAndView.addObject("loginBean",new LoginBean());
		return modelAndView;
	}
	
	/**
	 * 跳转到注册页面
	 * @return
	 */
	@GetMapping(value = "register")
	public ModelAndView register(boolean createAdmin) {
		ModelAndView modelAndView = new ModelAndView("register");
		RegisterBean registerBean = new RegisterBean();
		registerBean.setCreateAdmin(createAdmin);
		modelAndView.addObject("reigsterBean",registerBean);
		return modelAndView;
	}
	
	@PostMapping(value = "register")
	public ModelAndView doRegister(RegisterBean registerBean) {
		ModelAndView modelAndView = new ModelAndView();
		logger.debug("用户注册功能");
		if(registerBean.getPassword().equals(registerBean.getRetypePassword())) {
			UserInfo userInfo = new UserInfo();
			userInfo.setUsername(registerBean.getUsername());
			String password = DigestUtils.md5Hex(registerBean.getPassword());
			userInfo.setPassword(password);
			this.userInfoRepository.save(userInfo);
			UserAuthority userAuthority = new UserAuthority();
			userAuthority.setUserInfo(userInfo);
			userAuthority.setAuthority("ROLE_USER");
			this.userAuthorityRepository.save(userAuthority);
			if(registerBean.isCreateAdmin()) {
				userAuthority = new UserAuthority();
				userAuthority.setAuthority("ROLE_ADMIN");
				userAuthority.setUserInfo(userInfo);
				this.userAuthorityRepository.save(userAuthority);
			}
		}
		return modelAndView;
	}
	
	@PostMapping(value = "doLogin")
	public ModelAndView doLogin(LoginBean loginBean,String service,HttpServletResponse response,HttpServletRequest request) {
		logger.debug("开始执行用户登录");
		UsernamePasswordToken token = new UsernamePasswordToken(loginBean.getUsername(),loginBean.getPassword());
		Subject subject = SecurityUtils.getSubject();
		try {
			subject.login(token);	
		}catch(IncorrectCredentialsException e) {
			logger.debug("当前用户{}登录密码错误",loginBean.getUsername());
		}
		
		
		UserInfo userInfo = this.userInfoRepository.findByUsername(loginBean.getUsername());
		if(userInfo == null) {
			throw new IllegalArgumentException("用户名不存在");
		}
		userInfo.setEnabled(true);
		this.userInfoRepository.save(userInfo);
		String password = DigestUtils.md5Hex(loginBean.getPassword());
		if(!password.equals(userInfo.getPassword())){
			throw new IllegalArgumentException("密码错误");
		}
		
		RandomStringGenerator stringGenerator = new RandomStringGenerator.Builder().withinRange('a', 'z').build();
		String ticket = stringGenerator.generate(20);
		
		ticket = "ST-"+DigestUtils.md5Hex(ticket);
		logger.debug("生成Ticket值{},并保存到数据用于校验",ticket);
		WebUtils.setSessionAttribute(request, "ticket", ticket);
		
		TicketValidate ticketValidate = new TicketValidate();
		ticketValidate.setTicket(ticket);
		ticketValidate.setUser(userInfo.getUsername());
		ticketValidate.setUserId(userInfo.getId());
		this.ticketValidateRepository.save(ticketValidate);
		
		ModelAndView modelAndView = new ModelAndView();
		if(StringUtils.isNotBlank(service)) {
			RedirectView redirectView = new RedirectView(service);
			Map<String,Object> attributes = new HashMap<>();
			attributes.put("ticket", ticket);
			redirectView.setAttributesMap(attributes);
			
			modelAndView.setView(redirectView);
		}else {
			modelAndView.setView(new RedirectView("account/index",true));
		}
		return modelAndView;
	}
	
	@ResponseBody
	@RequestMapping(value = "proxyValidate")
	public Serializable proxyValidate(String ticket) {
		logger.debug("开始执行proxyValidate方法,将通过Ticket值【{}】来获取登录信息",ticket);
		CasServiceResponseAuthenticationSuccess validate = new CasServiceResponseAuthenticationSuccess();
		CasServiceResponseAuthenticationFailure failure = new CasServiceResponseAuthenticationFailure();
		TicketValidate ticketValidate = this.ticketValidateRepository.findOne(ticket);
		logger.debug("判断TicketValidate对象是否存在");
		if(ticketValidate == null) {
			failure.setDescription("Ticket已经过期");
			logger.debug("获取Ticket失败，失败的原因是:{}",failure.getDescription());
			return failure;
		}else {
			validate.setUser(ticketValidate.getUser());
			
			
			List<UserAuthority> authorities = this.userAuthorityRepository.findByUserInfo(new UserInfo(ticketValidate.getUserId()));
			Map<String, Object> attributes = new HashMap<>();
			List<String> role = new ArrayList<>();
			//测试数据
//			role.add("ROLE_ADMIN");
//			role.add("ROLE_USER");
			for(UserAuthority authority : authorities) {
				role.add(authority.getAuthority());
			}
			attributes.put("ROLE", role);
			validate.setAttributes(attributes);
			//this.ticketValidateRepository.delete(ticketValidate);
		}
		logger.debug("成功获取到Ticket信息返回请求服务,返回信息是:{}",validate.toString());
		return validate;
	}
	
	@RequestMapping(value = "error")
	public String error() {
		
		return "500";
	}
	
	@RequestMapping(value = "logout")
	public String logout() {
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		return "";
	}

}
